/*
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.net.Uri;

import com.onegravity.sudoku.solver.HintResource;
import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.core.IndirectHint;
import com.onegravity.sudoku.solver.core.Rule;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.ImmutableCell;
import com.onegravity.sudoku.solver.data.Region;
import com.onegravity.sudoku.solver.util.HtmlLoader;

/**
 * Sue de Coq
 * @author Emanuel Moecklin
 */
public class SueDeCoqHint extends IndirectHint implements Rule {

	private static final long serialVersionUID = 7692531675311227123L;
	
	private int nrOfIntersectionCells;
	private List<Cell> intersection;
	private Cell cell1;
	private Cell cell2; 
    private final Region region1;
    private final Region region2;

    private int cardinalityAll;
    private BitSet potentialsRest;
    private BitSet potentialsCell1;
    private BitSet potentialsCell2;
    
	private List<Cell> allCells;
    private Map<Cell, BitSet> selectedPotentialsRest;
    private Map<Cell, BitSet> selectedPotentials1;
    private Map<Cell, BitSet> selectedPotentials2;
    
	public SueDeCoqHint(List<Cell> intersection, Cell cell1, Cell cell2, Region region1, Region region2, Map<Cell, BitSet> removablePotentials) {
        super(removablePotentials);
        this.intersection = intersection;
        this.cell1 = cell1;
        this.cell2 = cell2;
        this.region1 = region1;
        this.region2 = region2;
		allCells = new ArrayList<Cell>(intersection);
		allCells.add(cell1);
		allCells.add(cell2);

		// compute potential related variables
    	potentialsRest = new BitSet();
    	for (Cell cell:intersection) {
    		potentialsRest.or(cell.getPotentialValues());
    	}
		potentialsCell1 = (BitSet) cell1.getPotentialValues().clone();
		potentialsCell2 = (BitSet) cell2.getPotentialValues().clone();
    	cardinalityAll = potentialsRest.cardinality();
		potentialsRest.andNot(potentialsCell1);
		potentialsRest.andNot(potentialsCell2);

		// compute selected potentials
		selectedPotentials1 = new HashMap<Cell, BitSet>();
		selectedPotentials1.put(cell1, potentialsCell1);
		
		selectedPotentials2 = new HashMap<Cell, BitSet>();
		selectedPotentials2.put(cell2, potentialsCell2);

		selectedPotentialsRest = new HashMap<Cell, BitSet>();
		nrOfIntersectionCells = 0;
		for (Cell cell:intersection) {
			if (cell.isEmpty()) {
				nrOfIntersectionCells++;
				BitSet tmp = (BitSet) cell.getPotentialValues().clone();
				tmp.andNot(potentialsCell1);
				tmp.andNot(potentialsCell2);
				selectedPotentialsRest.put(cell, tmp);
				
				tmp = (BitSet) cell.getPotentialValues().clone();
				tmp.and(potentialsCell1);
				if (!tmp.isEmpty()) selectedPotentials1.put(cell, tmp);
				
				tmp = (BitSet) cell.getPotentialValues().clone();
				tmp.and(potentialsCell2);
				if (!tmp.isEmpty()) selectedPotentials2.put(cell, tmp);
			}
		}
    }

    @Override
    public Region[] getRegions(HintType hintType) {
    	return (hintType==HintType.SMALL_CLUE) ? null : new Region[] {region1, region2};
    }

    @Override
    public Collection<Cell> getSelectedCells(HintType hintType) {
    	return (hintType==HintType.SMALL_CLUE) ? allCells : null;
    }

    @Override
    public Map<Cell, BitSet> getSelectedPotentials(HintType hintType, int viewNum) {
    	return (hintType==HintType.SMALL_CLUE) ? null : selectedPotentials1;
    }

    @Override
    public Map<Cell, BitSet> getSecondarySelectedPotentials(HintType hintType, int viewNum) {
    	return (hintType==HintType.SMALL_CLUE) ? null : selectedPotentials2;
    }

    @Override
    public Map<Cell, BitSet> getAddedPotentials(HintType hintType, int viewNum) {
    	return (hintType==HintType.SMALL_CLUE) ? null : selectedPotentialsRest;
    }

    @Override
    public Map<ImmutableCell, BitSet> getRemovedPotentials(HintType hintType) {
        return (hintType==HintType.HINT) ? super.getRemovedPotentials(hintType) : null;
    }

    @Override
    public double getDifficulty() {
    	return 5.0;
    }

    @Override
    public String getName() {
    	return HintResource.SUE_DE_COQ.getName();
    }

    @Override
    public Uri getLink() {
    	return HintResource.SUE_DE_COQ.getUrl();
	}

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof SueDeCoqHint)) return false;
        SueDeCoqHint other = (SueDeCoqHint)o;
        if (!this.intersection.equals(other.intersection)) return false;
        if (!this.cell1.equals(other.cell1)) return false;
        if (!this.cell2.equals(other.cell2)) return false;
        if (!this.region1.equals(other.region1)) return false;
        if (!this.region2.equals(other.region2)) return false;
        return true;
    }

    @Override
    public int hashCode() {
    	int hash = intersection.hashCode();
   		hash ^= cell1.hashCode();
   		hash ^= cell2.hashCode();
   		hash ^= region1.hashCode();
   		hash ^= region2.hashCode();
        return hash;
    }

    @Override
    public String getHintHtml(HintType hintType) {
    	int value1 =  potentialsCell1.nextSetBit(0);
    	int value2 =  potentialsCell1.nextSetBit(value1+1);
    	int value3 =  potentialsCell2.nextSetBit(0);
    	int value4 =  potentialsCell2.nextSetBit(value3+1);
    	int value5 =  potentialsRest.nextSetBit(0);
    	String value5String = (value5!=-1) ? (value5 + ", ") : ""; //$NON-NLS-1$ //$NON-NLS-2$
        String result = HtmlLoader.loadHtml(HintResource.SUE_DE_COQ.hint(hintType));
        return HtmlLoader.format(result, Cell.toString(allCells), Cell.toString(cell1), Cell.toString(cell2),
        						 value1, value2, value3, value4, value5String, region1.toFullString(), region2.toFullString(), cardinalityAll, nrOfIntersectionCells);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(getName());
        builder.append(": "); //$NON-NLS-1$
        builder.append(region1.toString());
        builder.append(region2.toString());
        return builder.toString();
    }

    @Override
    public int getNrOfSteps() {
        return 3;
    }

}